package com.sfac.springboot.modules.account.service.impl;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import javax.transaction.Transactional;

import com.sfac.springboot.modules.account.dao.UserDao;
import com.sfac.springboot.modules.account.dao.UserRoleDao;
import com.sfac.springboot.modules.account.entity.User;
import com.sfac.springboot.modules.account.entity.UserRole;
import com.sfac.springboot.modules.account.service.UserService;
import com.sfac.springboot.modules.common.vo.Result;
import com.sfac.springboot.modules.common.vo.Search;
import com.sfac.springboot.utils.MD5Util;
import com.sfac.springboot.utils.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

/**
 * @Description: User Service Impl
 * @author: HymanHu
 * @date: 2021年2月21日
 */
@Service
public class UserServiceImpl implements UserService {
	public final static Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

	@Autowired
	private UserDao userDao;
	@Autowired
	private UserRoleDao userRoleDao;
	@Autowired
	private RedisUtils redisUtils;

	@Override
	public User getUserByUserNameAndPassword(String userName, String password) {
		return userDao.getUserByUserNameAndPassword(userName, MD5Util.getMD5(password));
	}

	@Override
	public User getUserByUserName(String userName) {
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserName(userName))
				.orElse(Collections.emptyList());
		return users.isEmpty() ? null : users.get(0);
	}

	@Override
	public Result<User> login(User user) {
		//获取连续错误登录次数，超过三次则返回错误消息，没有超过3此，则继续登录
		String errorLoginKey = String.format("errorLoginCount%s",user.getUserName());
		int count =  redisUtils.get(errorLoginKey) == null
				? 0 : (Integer)redisUtils.get(errorLoginKey);
		if (count > 3){
			return  new Result<>(Result.ResultStatus.FAILED.code,
					"你已经连续错误登录3次，账户被锁定十五秒！");

		}


		//得到subject
		Subject subject = SecurityUtils.getSubject();

		//封装一个登录令牌(装在用户名和密码)
		UsernamePasswordToken token = new UsernamePasswordToken(
				user.getUserName(),
				MD5Util.getMD5(user.getPassword()));

		try{
			//身份认证和资源授权
			subject.login(token);
			subject.checkRoles();

			//获取当前用户，并将其设置到 session 中
			User temp = (User) subject.getPrincipal();
			Session session = subject.getSession();
			session.setAttribute("user",temp);

			//将错误登录次数置0
			redisUtils.set(errorLoginKey,0);


			return new Result<User>(Result.ResultStatus.SUCCESS.code,"Success");
		}catch (Exception e){
			e.printStackTrace();
			LOGGER.debug(e.getMessage());

			//将错误登录次数加1
			redisUtils.increment(errorLoginKey,1);
			//判断错误登录次数是否大于3次，是则设置该key过期时间
			count =  redisUtils.get(errorLoginKey) == null
					? 0 : (Integer)redisUtils.get(errorLoginKey);
			String errorMsg = "";
			if (count < 3){
				errorMsg = String.format("你已经连续错误登录%d 次,还有%d次",count,(3-count));
			}else {
				errorMsg = "你已经连续错误登录3次,锁定账户15秒";
				redisUtils.expire(errorLoginKey,15);
			}
			return new Result<>(Result.ResultStatus.FAILED.code,errorMsg);
		}
	}

	@Override
	public void logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
	}

	@Override
	@Transactional
	public Result<User> insertUser(User user) {
		if (StringUtils.isBlank(user.getUserName()) ||
				StringUtils.isBlank(user.getPassword())) {
			return new Result<User>(Result.ResultStatus.FAILED.code,
					"User Name or password is null.");
		}
		
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserName(user.getUserName()))
				.orElse(Collections.emptyList());
		if (users.size() > 0) {
			return new Result<User>(Result.ResultStatus.FAILED.code,
					"User Name or email is repeat.");
		}
		
		user.setCreateDate(LocalDateTime.now());
		user.setUpdateDate(LocalDateTime.now());
		user.setPassword(MD5Util.getMD5(user.getPassword()));
		userDao.insertUser(user);
		if (user.getRoles() != null) {
			user.getRoles().stream()
				.forEach(item -> {userRoleDao.insertUserRole(
						new UserRole(user.getId(), item.getId()));});
		}
		return new Result<User>(
				Result.ResultStatus.SUCCESS.code, "Insert success", user);
	}

	@Override
	@Transactional
	public Result<User> updateUser(User user) {
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserNameOrEmail(
						user.getEmail(), user.getUserName()))
				.orElse(Collections.emptyList());
		if (users.stream().filter(item -> item.getId() != user.getId()).count() > 0) {
			return new Result<User>(Result.ResultStatus.FAILED.code,
					"User Name or email is repeat.");
		}
		
		userDao.updateUser(user);
		if (user.getRoles() != null && !user.getRoles().isEmpty()) {
			userRoleDao.deleteUserRoleByUserId(user.getId());
			user.getRoles().stream()
				.forEach(item -> {userRoleDao.insertUserRole(
						new UserRole(user.getId(), item.getId()));});
		}
		
		return new Result<User>(Result.ResultStatus.SUCCESS.code,
				"Update success", user);
	}

	@Override
	public User getUserById(int id) {
		return userDao.getUserById(id);
	}

	@Override
	@Transactional
	public Result<Object> deleteUserById(int id) {
		userDao.deleteUserById(id);
		userRoleDao.deleteUserRoleByUserId(id);
		return new Result<Object>(Result.ResultStatus.SUCCESS.code, "Delete success");
	}

	@Override
	public PageInfo<User> getUsersBySearchBean(Search search) {
		search.init();
		PageHelper.startPage(search.getCurrentPage(), search.getPageSize());
		return new PageInfo<User>(Optional
				.ofNullable(userDao.getUsersBySearchVo(search))
				.orElse(Collections.emptyList()));
	}

	@Override
	public List<User> getUsersByKeyWord(String keyword) {
		Search search = new Search();
		search.setKeyword(keyword);
		search.init();
		return Optional.ofNullable(userDao.getUsersBySearchVo(search))
				.orElse(Collections.emptyList());
	}
}
